#ifndef HMS_VELOCITY_TRANSFORMER_HPP
#define HMS_VELOCITY_TRANSFORMER_HPP

#include "typeAliases.hpp"
#include "field.hpp"
#include <experimental/memory>

namespace hms
{


/**
 * @brief Holds memory for coordinate transformation.
 * Intended to use as member variable for boundary conditions that require
 * velocity transformations.
 * 
 */
class CoordTransformer {
private:
	Index m_size {0};
	mutable Array2Xs m_transformBuf;
	mutable Array2Xs m_cartesianBuf;

	void checkSize() const;

public:
	CoordTransformer() = default;
	CoordTransformer( Index size );

	void resize( Index size );
	auto size() const -> Index;

	auto transformed() const -> Array2Xs&;
	auto cartesian  () const -> Array2Xs&;

	auto transform(
		const Ref<const Array2Xs>& cartesian,
		const Ref<const Array2Xs>& normals,
		bool orthoOnly = false
	) const -> Array2Xs&;

	auto retransform(
		const Ref<const Array2Xs>& transformed,
		const Ref<const Array2Xs>& normals
	) const -> Array2Xs&;
};


using std::experimental::observer_ptr;

/**
 * @brief Convenience wrapper around CoordTransformer
 * for use with BoundaryConditions.
 * When the same Field and BoundaryPatch are passed to it during construction
 * as to the BoundaryCondition, then transform() and retransform() use these
 * to pass the appropriate data to the CoordTransformer.
 * 
 * @tparam MeshType 
 */
template<typename MeshType>
class FieldTransformer {
private:
	CoordTransformer m_transformer;

	observer_ptr<const Field<MeshType>> m_field {nullptr};
	observer_ptr<const BoundaryPatch> m_patch {nullptr};

public:
	FieldTransformer() = default;

	FieldTransformer( const Field<MeshType>&, const BoundaryPatch& );

	auto field() const -> const Field<MeshType>&;
	auto patch() const -> const BoundaryPatch&;
	auto normals() const -> decltype(auto);
	auto internal() const -> const CoordTransformer&;

	auto transformed() const -> Array2Xs&;
	auto cartesian  () const -> Array2Xs&;

	auto transform(
		bool ghost     = false,
		bool orthoOnly = false
	) const -> Array2Xs&;

	auto retransform( const Ref<const Array2Xs>& transformed ) const -> Array2Xs&;
	auto retransform() const -> Array2Xs&;

private:
	void field( const Field<MeshType>& );
	void patch( const BoundaryPatch& );
};

} // namespace hms

#endif